package io.github.source.config;

import com.github.catvod.crawler.Spider;
import com.github.catvod.crawler.SpiderNull;
import com.github.catvod.net.OkHttp;

import io.github.source.bean.Config;
import io.github.source.bean.Doh;
import io.github.source.bean.Parse;
import io.github.source.bean.Rule;
import io.github.source.bean.Site;
import io.github.source.config.impl.Callback;
import io.github.source.config.impl.ICallback;
import io.github.source.util.UrlUtil;

import com.github.catvod.utils.Json;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.orhanobut.hawk.Hawk;
import com.orhanobut.logger.Logger;

import org.json.JSONObject;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @ClassName: VodConfig
 * @Description: TODO(站源配置) #公用的#单例
 * @author: huangrongcai
 * @date: 2024/6/4
 * @Copyright: 黄嵘才（HuangRc)
 */
public class VodConfig {
    /**
     * 初始成功
     */
    public final static int VOD_CODE_SUCCESS = 200;
    public final static int VOD_CODE_FAILED = 400;
    public final static int VOD_CODE_TIMEOUT = 405;
    private static final String TAG = "Source.VodConfig";
    private WeakReference<ExecutorService> executorRef;
    private List<Doh> doh;
    private List<Rule> rules;
    private List<Site> sites;
    private List<Parse> parses;
    private List<String> flags;
    private List<String> ads;
    private JarLoader jarLoader;
    private PyLoader pyLoader;
    private JsLoader jsLoader;
    private boolean loadLive;
    private Config config;
    private Parse parse;
    private String wall;
    private Site home;
    /**
     * 标记本配置是不是完成初始
     */
    private final AtomicBoolean isInitialized = new AtomicBoolean(false);

    private VodConfig() {
        //私有构造函数
    }

    private static class Loader {
        static volatile VodConfig INSTANCE = new VodConfig();
    }

    public static VodConfig get() {
        return Loader.INSTANCE;
    }


    public void setApi(String apiUrl) {
        Hawk.put(HawkConfig.API_URL, apiUrl);
    }


    public static int getCid() {
        return get().getConfig().getId();
    }

    public static String getUrl() {
        return get().getConfig().getUrl();
    }

    public static String getDesc() {
        return get().getConfig().getDesc();
    }

    public static int getHomeIndex() {
        return get().getSites().indexOf(get().getHome());
    }


    /**
     * 首页源站点
     *
     * @return 首页源站点
     */
    public Site getHome() {
        return home == null ? new Site() : home;
    }

    public Config getConfig() {
        return config == null ? Config.vod() : config;
    }

    public List<String> getFlags() {
        return flags == null ? Collections.emptyList() : flags;
    }


    public static boolean checkInitialized() {
        if (VodConfig.get().isInitialized.get()) {
            return true;
        }

        return false;
    }

    private boolean isUseCache() {
        boolean shouldCache = false;
        if (config != null && config.getJson() != null) {
            String string = config.getJson(); // 获取配置中的 JSON 数据
            shouldCache = !string.isEmpty(); // 如果 JSON 数据不为空，则启用缓存
        }
        return shouldCache;
    }

    /**
     * 初始化配置（默认配置）
     * 需要访问Context 做一检查
     */
    public void initConfig() {
        initConfig(getCallback());
    }

    public void initConfigSync() {
        initConfigSync(getCallback());
    }

    private void initConfigSync(ICallback callback) {
        loadSync(callback);
    }

    /**
     * 取配置文件进行应用配置(如果已初始化过的，将复用）
     *
     * @param callback 配置结果回调
     */
    public void initConfig(ICallback callback) {
        loadAsync(callback);
    }

    /**
     * 取配置文件进行应用配置(如果已初始化过的，将复用）
     *
     * @param callback 配置结果回调
     */
    public static void initConfig(Config config, ICallback callback) {
        get().config(config).initConfig(callback);
    }

    public VodConfig clear() {
        this.wall = null;
        this.home = null;
        this.parse = null;
        this.config = Config.vod();
        this.ads = new ArrayList<>();
        this.doh = new ArrayList<>();
        this.rules = new ArrayList<>();
        this.sites = new ArrayList<>();
        this.flags = new ArrayList<>();
        this.parses = new ArrayList<>();
        this.jarLoader = new JarLoader();
        this.pyLoader = new PyLoader();
        this.jsLoader = new JsLoader();
        this.loadLive = true;
        return this;
    }

    public VodConfig config(Config config) {
        this.config = config;
        return this;
    }

    private Future<?> future;

    private void loadSync(ICallback callback) {
        if (checkInitialized()) {
            callback.onCallback(VOD_CODE_SUCCESS, "已初始化源配置成功");
            return;
        }
        new InitTask(callback).run();
    }

    //异步加20秒超进
    private void loadAsync(ICallback callback) {
        if (checkInitialized()) {
            callback.onCallback(VOD_CODE_SUCCESS, "初始化源配置成功");
            return;
        }
        try {
            if (executorRef == null || executorRef.get() == null) {
                //设置单线程池，是为保障配置文件加载的线程安全
                ExecutorService service = Executors.newSingleThreadExecutor();
                executorRef = new WeakReference<>(service);
            }

            future = executorRef.get().submit(new InitTask(callback));
            // 设置超时时间
            future.get(20, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            future.cancel(true); // 尝试取消任务
            Logger.t(TAG).w("任务超时");
            if (callback != null) {
                callback.onCallback(VOD_CODE_TIMEOUT, "任务超时");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // 重新设置中断标志
            Logger.t(TAG).w("任务被中断", e);

            if (callback != null) {
                callback.onCallback(400, "任务被中断");
            }
        } catch (ExecutionException e) {
            Logger.t(TAG).e("执行异常", e);
            if (callback != null) {
                callback.onCallback(400, "执行异常");
            }
        } catch (CancellationException e) {
            Logger.t(TAG).w("任务被取消", e);
            if (callback != null) {
                callback.onCallback(400, "任务被取消");
            }
        }

    }

    private void handleParsingError(ICallback callback) {
        if (callback != null) {
            callback.onCallback(400, "解析失败");
        }
    }

    private void handleUnexpectedError(ICallback callback, Exception e) {
        Logger.w("UnexpectedError:" + e);
        if (config.getUrl() != null) {
            loadCache(callback);
        } else {
            callback.onCallback(400, "未知错误");
        }
    }

    private void loadCache(ICallback callback) {
        //检查配置对象
        if (config.getJson() != null) {
            parseConfig(Json.parse(config.getJson()).getAsJsonObject(), callback);
        } else {
            //post error
            handleParsingError(callback);
        }
    }

    /**
     * 解析配置数据
     *
     * @param object   配置源数据（json对象）
     * @param callback 回调
     */
    private void parseConfig(JsonObject object, ICallback callback) {
        try {

            // 优化性能：避免直接转换整个JsonObject为字符串
            config.json(object.toString()).update();

            // 使用日志记录代替直接打印堆栈跟踪
            if (callback != null) {
                callback.onCallback(VOD_CODE_SUCCESS, "初始化成功");
            }
        } catch (Exception e) { // 捕获Exception而非Throwable
            Logger.e(e, "Failed to parse configuration");
            // 维持原有的逻辑，使用callback通报错误
            handleParsingError(callback);
        }
    }


    private static void setHomeSiteBasedOnConfig() {
        // 遍历sites集合，设置首页站源
        for (Site site : get().sites) {
            if (site.getKey().equals(get().config.getHome())) {
                get().setHome(site);
                break; // 如果找到匹配的站点，就不需要继续遍历了
            }
        }

    }

    private static void initMultipleSites(JsonObject object) {
        //解析源站点
        List<JsonElement> elements = Json.safeListElement(object, "sites");
        List<Site> pendingSite = new ArrayList<>();
        for (JsonElement element : elements) {
            try {
                Site site = Site.objectFrom(element);
                if (!get().sites.contains(site)) { // 仍然使用HashSet的contains方法
                    site.setApi(parseApi(site.getApi()));
                    site.setExt(parseExt(site.getExt()));
                    pendingSite.add(site.sync());
                }
            } catch (Exception e) {
                // 异常处理逻辑，例如记录日志等
                Logger.t(TAG).e(e, "Error initializing site from JSON element: " + e.getMessage());
            }
        }
        synchronized (get()) {
            get().sites.addAll(pendingSite);
        }
    }


    private static String parseApi(String api) {
        if (api.startsWith("file") || api.startsWith("clan") || api.startsWith("assets"))
            return UrlUtil.convert(api);
        return api;
    }

    private static String parseExt(String ext) {
        if (ext.startsWith("file") || ext.startsWith("clan") || ext.startsWith("assets"))
            return UrlUtil.convert(ext);
        if (ext.startsWith("img+")) return Decoder.getExt(ext);
        return ext;
    }

    private Callback getCallback() {
        return new Callback() {
        };
    }

    /**
     * 获取爬虫
     *
     * @param site 源站点
     * @return Spider
     */
    public Spider getSpider(Site site) {
        if (site == null || site.getApi() == null) return new SpiderNull();

        boolean js = site.getApi().contains(".js");
        boolean py = site.getApi().contains(".py");
        boolean csp = site.getApi().startsWith("csp_");
        Spider spider;
        if (py) {
            spider = pyLoader.getSpider(site.getKey(), site.getApi(), site.getExt());
        } else if (js) {
            spider = jsLoader.getSpider(site.getKey(), site.getApi(), site.getExt(), site.getJar());
        } else if (csp) {
            spider = jarLoader.getSpider(site.getKey(), site.getApi(), site.getExt(), site.getJar());
        } else {
            spider = new SpiderNull();
        }
        Logger.t(TAG).d("getSpider: " + spider);
        return spider;
    }

    public Spider getSpider(String siteKey) {
        return getSpider(getSite(siteKey));
    }

    /**
     * 获取源站点
     *
     * @param key 源站点Key Id
     * @return 源站点
     */
    public Site getSite(String key) {
        try {
            int index = getSites().indexOf(Site.get(key));
            Site site;
            if (index == -1) {
                site = new Site();
            } else {
                site = getSites().get(index);
            }
            return site;
        } catch (Exception e) {
            Logger.e(e, "Error getting site: " + e.getMessage());
            return new Site();
        }
    }

    /**
     * 获取常用的
     */
    public Site getSite() {
        //TOdo 设计一个评价系统，区分哪一些站点的连接速度会更快（在更新源时自动选择最优）
        return getSite(config.getHome());
    }

    public List<Site> getSites() {
        return sites == null ? Collections.emptyList() : sites;
    }

    public List<Parse> getParses(int type) {
        List<Parse> items = new ArrayList<>();
        for (Parse item : getParses()) if (item.getType() == type) items.add(item);
        return items;
    }

    public List<Parse> getParses(int type, String flag) {
        List<Parse> items = new ArrayList<>();
        for (Parse item : getParses(type))
            if (item.getExt().getFlag().isEmpty() || item.getExt().getFlag().contains(flag))
                items.add(item);
        if (items.isEmpty()) items.addAll(getParses(type));
        return items;
    }

    public List<Parse> getParses() {
        return parses == null ? Collections.emptyList() : parses;
    }

    public void setParse(Parse parse) {
        this.parse = parse;
        this.parse.setActivated(true);
        config.parse(parse.getName()).save();
        for (Parse item : getParses()) item.setActivated(parse);
    }

    public Parse getParse() {
        return parse == null ? new Parse() : parse;
    }

    public Parse getParse(String name) {
        int index = getParses().indexOf(Parse.get(name));
        return index == -1 ? null : getParses().get(index);
    }

    public List<Rule> getRules() {
        return rules == null ? Collections.emptyList() : rules;
    }

    public void setHome(Site home) {
        Logger.t(TAG).d("配置首页: " + home);
        this.home = home;
        this.home.setActivated(true);
        config.home(home.getKey()).save();
        for (Site item : getSites()) item.setActivated(home);
    }

    private void setWall(String wall) {
        //设置壁纸
        this.wall = wall;
    }

    public static boolean hasParse() {
        return get().getParses().size() > 0;
    }

    public static boolean containFlag(String flag) {
        return get().getFlags().contains(flag);
    }

    /**
     * ？这是做什么用的？
     *
     * @param key key
     * @param jxs jxs
     * @param url url
     * @return JSONObject
     * @throws Throwable
     */
    public JSONObject jsonExt(String key, LinkedHashMap<String, String> jxs, String url) throws Throwable {
        return jarLoader.jsonExt(key, jxs, url);
    }

    /**
     * ？
     *
     * @param flag flag
     * @param key  key
     * @param name name
     * @param jxs  jxs
     * @param url  url
     * @return JSONObject
     * @throws Throwable
     */
    public JSONObject jsonExtMix(String flag, String key, String name, LinkedHashMap<String, HashMap<String, String>> jxs, String url) throws Throwable {
        return jarLoader.jsonExtMix(flag, key, name, jxs, url);
    }

    public void setDoh(List<Doh> doh) {
        this.doh = doh;
    }

    public void setRules(List<Rule> rules) {
        for (Rule rule : rules)
            if ("proxy".equals(rule.getName())) OkHttp.selector().setHosts(rule.getHosts());
        rules.remove(Rule.create("proxy"));
        this.rules = rules;
    }

    private void setFlags(List<String> flags) {
        this.flags.addAll(flags);
    }

    public List<String> getAds() {
        return ads == null ? Collections.emptyList() : ads;
    }

    private void setAds(List<String> ads) {
        this.ads = ads;
    }

    /**
     * 设置当前使用的源站点（当前解析爬虫使用的源站点）
     *
     * @param site Site 源站点
     */
    public void setRecent(Site site) {
        if (site == null || site.getApi() == null) return;
        boolean js = site.getApi().contains(".js");
        boolean py = site.getApi().contains(".py");
        boolean csp = site.getApi().startsWith("csp_");
        if (js) jsLoader.setRecent(site.getKey());
        else if (py) pyLoader.setRecent(site.getKey());
        else if (csp) jarLoader.setRecent(site.getJar());
    }

    /**
     * 初始化任务
     */
    private static class InitTask implements Runnable {
        private Config config = null;
        private long startInitAT = 0;
        private String TAG = "Source.InitTask";
        private final ICallback callback;
        private final AtomicBoolean canceled = new AtomicBoolean(false);

        public InitTask(ICallback callback) {
            this.callback = callback;
        }

        public Config defaultConfig() {
            String apiUrl = Hawk.get(HawkConfig.API_URL, "");//配置API
            TAG = TAG + "." + apiUrl;
            return Config.vod().url(apiUrl);
        }

        @Override
        public void run() {
            //检查没有初始化，加锁（+初始化）：再检查没有初始化，进行初始化，
            executeInit();
        }

        private boolean executeInit() {
            try {
                traceStartInit();

                if (!checkInitialized()) {//false
                    synchronized (get().isInitialized) {
                        if (!checkInitialized()) {
                            startInit();
                        }
                    }
                }
                //END
                traceEndInit();

                VodConfig.get().isInitialized.set(true);
                handleInitSuccess("成功");
                return true;
            } catch (Exception e) {
                VodConfig.get().isInitialized.set(false);
                // 记录加载配置过程中的其他未知异常
                handleInitError(e, "加载配置过程中出现未知错误， " + e.getMessage());
                return false;
            } finally {
                Logger.t(TAG).d("加载配置文件结束， 结果：" + get().isInitialized);

            }

        }

        private void startInit() throws Exception {
            VodConfig instance = get();
            // 执行清理操作
            instance.clear();

            // 设置配置
            config = defaultConfig();
            instance.config(config);
            boolean shouldCache = instance.isUseCache();

            String configStr = "";
            if (shouldCache) {
                configStr = getConfigJsonFromCache();
            } else {
                configStr = Decoder.getJson(config.getUrl());
            }
            // 日志记录获取到的配置数据，用于调试
            Logger.t(TAG).d("配置数据: " + configStr);
            // 解析JSON字符串为JsonObject，并调用回调函数处理解析后的配置
            JsonObject object = Json.parse(configStr).getAsJsonObject();
            initSites(object);
            initSpider(object);
            initParses(object);
            initDoh(object);
            initRules(object);
            initOther(object);
            initLive(object);
            initDrives(object);
            // 优化性能：避免直接转换整个JsonObject为字符串
            saveConfig(object);
            //检查一下是不理初始化成功了
            checkAndVerifyInitDone();

        }

        private void handleInitError(Throwable e, String msg) {
            Logger.t(TAG).e(e, "加载配置过程中出现未知错误 " + e.getMessage());
            boolean result = get().isInitialized.get();
            if (callback != null) {
                callback.onCallback(result ? VOD_CODE_SUCCESS : VOD_CODE_FAILED, msg);
            }
        }

        private void handleInitSuccess(String msg) {
            boolean result = get().isInitialized.get();
            if (callback != null) {
                // VOD_CODE_SUCCESS 表示初始化成功，VOD_CODE_FAILED 表示初始化失败
                callback.onCallback(result ? VOD_CODE_SUCCESS : VOD_CODE_FAILED, msg);
            }
        }


        private String getConfigJsonFromCache() {
            String string = "";//返回的数据
            if (config != null && config.getJson() != null && !config.isEmpty()) {
                string = config.getJson();
            }
            return string;
        }

        private void initSites(JsonObject object) {
            //===点播源站点
            try {
                if (object.has("video")) {
                    initSites(object.getAsJsonObject("video"));
                    return;
                }
                initMultipleSites(object);
                //设置首页站源
                setHomeSiteBasedOnConfig();
            } catch (Exception e) {
                Logger.t(TAG).e(e, "Failed to parse configuration initSites");
            }
        }

        private void initParses(JsonObject object) {
            try {
                for (JsonElement element : Json.safeListElement(object, "parses")) {
                    Parse parse = Parse.objectFrom(element);
                    if (parse.getName().equals(config.getParse()) && parse.getType() > 1)
                        get().setParse(parse);
                    if (!get().parses.contains(parse)) get().parses.add(parse);
                }
            } catch (Exception e) {
                Logger.t(TAG).e(e, "Failed to parse configuration initParses");
            }

        }

        private void initDoh(JsonObject object) {
            try {
                get().setDoh(Doh.arrayFrom(object.getAsJsonArray("doh")));
            } catch (Exception e) {
                Logger.t(TAG).e(e, "Failed to parse configuration initDoh");
            }

        }

        private void initRules(JsonObject object) {
            try {
                get().setRules(Rule.arrayFrom(object.getAsJsonArray("rules")));
            } catch (Exception e) {
                Logger.t(TAG).e(e, "Failed to parse configuration initRules");
            }

        }

        private void initOther(JsonObject object) {
            try {
                VodConfig instance = get();
                if (instance.parses.size() > 0) instance.parses.add(0, Parse.god());
                if (instance.home == null)
                    instance.setHome(instance.sites.isEmpty() ? new Site() : instance.sites.get(0));
                if (instance.parse == null)
                    instance.setParse(instance.parses.isEmpty() ? new Parse() : instance.parses.get(0));
                instance.setFlags(Json.safeListString(object, "flags"));
                instance.setWall(Json.safeString(object, "wallpaper"));
                instance.setAds(Json.safeListString(object, "ads"));
                String warningText = Json.safeString(object, "warningText");
                String logo = Json.safeString(object, "logo");
                config.logo(logo);
            } catch (Exception e) {
                Logger.t(TAG).e(e, "Failed to parse configuration initOther");
            }

        }

        private void initLive(JsonObject object) {
            //===直播源站点
            try {
                if (get().loadLive && object.has("lives")) {
                    Config temp = Config.find(config, 1).save();
                    boolean sync = LiveConfig.get().needSync(get().getUrl());
                    if (sync) {
                        LiveConfig.get().clear().config(temp).parse(object);
                    }
                }
            } catch (Exception e) {
                Logger.t(TAG).e(e, "Failed to parse configuration initLive");
            }

        }

        private void initDrives(JsonObject object) {
            try {
                if (object.has("drives")) {
                    // TODO do something
                }
            } catch (Exception e) {
                Logger.t(TAG).e(e, "Failed to parse configuration initDrives");
            }

        }

        private void initSpider(JsonObject object) {
            try {
                //初始化解析爬虫的配置信息
                String spider = Json.safeString(object, "spider");
                if (spider == null || spider.trim().isEmpty()) {
                    Logger.t(TAG).w("Empty or null 'spider' parameter.");
                    return;
                }
                get().jarLoader.parseJar("", spider); // 假设valid_prefix是安全的前缀
            } catch (Exception e) {
                Logger.t(TAG).e(e, "Failed to parse configuration initSpider");
            }

        }

        private void saveConfig(JsonObject object) {
            try {
                //初始化解析爬虫的配置信息

                if (object == null || object.isEmpty()) {
                    Logger.t(TAG).w("Empty or null 'Config' parameter.");
                    return;
                }
                // 优化性能：避免直接转换整个JsonObject为字符串
                config.json(object.toString()).update();
            } catch (Exception e) {
                Logger.t(TAG).e(e, "Failed to save configuration initSpider");
            }

        }

        private void checkAndVerifyInitDone() {
            // 检查初始化的结果，如果首页索引为 -1，则表示初始化失败
            if (getHomeIndex() == -1) {
                get().isInitialized.set(false);
            }
        }

        /**
         * 打印初始化开始和结束的时间戳
         */
        private void traceStartInit() {
            startInitAT = System.currentTimeMillis();
            Logger.t(TAG).d(" --> start vod config init. time：" + startInitAT);
        }

        /**
         * 打印初始化开始和结束的时间戳
         */
        private void traceEndInit() {
            long endInitAT = System.currentTimeMillis();
            Logger.t(TAG).d(" <-- end vod config init. time：" + endInitAT + "， costed:" + (endInitAT - startInitAT));
        }
    }
}
